package com.forum.message.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.forum.common.event.MessageEvent;
import com.forum.common.exception.BusinessException;
import com.forum.message.feign.UserFeignClient;
import com.forum.message.mapper.MessageMapper;
import com.forum.message.model.dto.MessageDTO;
import com.forum.message.model.entity.Message;
import com.forum.message.service.MessageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 消息服务实现类
 */
@Slf4j
@Service
public class MessageServiceImpl implements MessageService {
    
    private final MessageMapper messageMapper;
    private final UserFeignClient userFeignClient;
    
    // 将RedisTemplate设为可选依赖
    private RedisTemplate<String, Object> redisTemplate;
    
    // Redis是否可用的标志
    private boolean redisEnabled = false;
    
    private static final String UNREAD_COUNT_KEY = "message:unread_count:";
    private static final String UNREAD_TYPE_COUNT_KEY = "message:unread_count:";
    
    @Autowired
    public MessageServiceImpl(MessageMapper messageMapper, UserFeignClient userFeignClient, 
                             @Autowired(required = false) RedisTemplate<String, Object> redisTemplate) {
        this.messageMapper = messageMapper;
        this.userFeignClient = userFeignClient;
        this.redisTemplate = redisTemplate;
        
        // 检查Redis是否可用
        this.redisEnabled = (redisTemplate != null);
        if (!redisEnabled) {
            log.warn("Redis不可用，将使用数据库进行消息计数");
        } else {
            log.info("Redis可用，将使用Redis进行消息计数缓存");
        }
    }
    
    @Override
    @Transactional
    public Message processMessageEvent(MessageEvent event) {
        log.info("开始处理消息事件，事件类型: {}, 发送者: {}, 接收者: {}", 
                event.getType(), event.getSenderId(), event.getReceiverId());
        
        try {
            // 检查事件是否有效
            if (event == null) {
                log.error("收到空事件");
                return null;
            }
            
            // 检查是否是重复消息
            if (isDuplicateMessage(event)) {
                log.info("检测到重复消息，跳过处理");
                return null;
            }
            
            // 构建消息实体
            log.info("构建消息实体");
            Message message = buildMessageFromEvent(event);
            
            // 保存消息
            log.info("准备保存消息实体到数据库");
            Message savedMessage = saveMessage(message);
            
            // 这里可以添加额外的处理，如通过WebSocket推送实时通知
            log.info("消息事件处理完成，生成的消息ID: {}", savedMessage != null ? savedMessage.getId() : "null");
            
            return savedMessage;
        } catch (Exception e) {
            log.error("处理消息事件失败: {}", e.getMessage(), e);
            throw e; // 重新抛出异常以触发事务回滚
        }
    }
    
    @Override
    @Transactional
    public Message saveMessage(Message message) {
        log.info("开始保存消息，详细信息: {}", message);
        
        try {
            // 检查消息是否有效
            if (message == null) {
                log.error("尝试保存空消息");
                return null;
            }
            
            // 检查接收者是否存在
            try {
                log.info("检查接收者是否存在: {}", message.getReceiverId());
                Boolean exists = userFeignClient.checkUserExists(message.getReceiverId());
                if (Boolean.FALSE.equals(exists)) {
                    log.warn("消息接收者不存在，消息ID: {}, 接收者ID: {}", message.getId(), message.getReceiverId());
                }
            } catch (Exception e) {
                log.warn("检查用户存在性失败，将继续保存消息: {}", e.getMessage());
            }
            
            log.info("开始执行消息保存操作");
            // MyBatis-Plus会自动填充创建时间
            messageMapper.insert(message);
            log.info("消息保存成功，ID: {}", message.getId());
            
            // 如果Redis可用，更新未读消息计数缓存
            if (redisEnabled && redisTemplate != null) {
                try {
                    log.info("更新Redis中的未读消息计数");
                    String userCountKey = UNREAD_COUNT_KEY + message.getReceiverId();
                    redisTemplate.opsForValue().increment(userCountKey);
                    redisTemplate.expire(userCountKey, 1, TimeUnit.DAYS);
                    
                    if (message.getType() != null) {
                        String typeCountKey = UNREAD_TYPE_COUNT_KEY + message.getReceiverId() + ":" + message.getType();
                        redisTemplate.opsForValue().increment(typeCountKey);
                        redisTemplate.expire(typeCountKey, 1, TimeUnit.DAYS);
                    }
                    log.info("Redis未读消息计数更新成功");
                } catch (Exception e) {
                    log.error("更新Redis未读消息计数失败: {}", e.getMessage(), e);
                }
            } else {
                log.info("Redis未启用或不可用，跳过未读计数缓存更新");
            }
            
            return message;
        } catch (Exception e) {
            log.error("保存消息到数据库失败，详细信息: {}, 错误: {}", message, e.getMessage(), e);
            throw e; // 重新抛出异常以触发事务回滚
        }
    }
    
    @Override
    public org.springframework.data.domain.Page<MessageDTO> getUserMessages(Long userId, Boolean isRead, String type, Pageable pageable) {
        log.info("获取用户消息列表, userId={}, isRead={}, type={}", userId, isRead, type);
        
        try {
            // 创建MyBatis-Plus分页对象
            com.baomidou.mybatisplus.extension.plugins.pagination.Page<Message> page = 
                new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageable.getPageNumber() + 1, pageable.getPageSize());
            
            // 调用Mapper的自定义查询方法
            com.baomidou.mybatisplus.core.metadata.IPage<Message> messagePage = 
                messageMapper.selectUserMessages(page, userId, isRead, type);
            
            // 将结果转换为DTO
            List<MessageDTO> dtoList = messagePage.getRecords().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
            
            // 返回Spring Data Page对象
            return new PageImpl<>(dtoList, pageable, messagePage.getTotal());
        } catch (Exception e) {
            log.error("获取用户消息失败: {}", e.getMessage(), e);
            return new PageImpl<>(List.of(), pageable, 0);
        }
    }
    
    @Override
    @Transactional
    public MessageDTO markAsRead(Long messageId, Long userId) {
        log.info("标记消息为已读: messageId={}, userId={}", messageId, userId);
        
        // 获取消息
        Message message = messageMapper.selectById(messageId);
        if (message == null) {
            throw new BusinessException("消息不存在");
        }
        
        // 验证消息归属
        if (!message.getReceiverId().equals(userId)) {
            throw new BusinessException("无权操作此消息");
        }
        
        // 已读则无需更改
        if (message.getIsRead()) {
            return convertToDTO(message);
        }
        
        // 更新消息状态
        message.setIsRead(true);
        message.setReadTime(LocalDateTime.now());
        messageMapper.updateById(message);
        
        // 只有在Redis可用时才更新Redis缓存
        if (redisEnabled && redisTemplate != null) {
            try {
                // 更新Redis中的未读消息计数
                String redisKey = UNREAD_COUNT_KEY + userId;
                redisTemplate.opsForValue().decrement(redisKey);
                
                // 更新特定类型的未读消息计数
                if (message.getType() != null) {
                    String typeRedisKey = UNREAD_TYPE_COUNT_KEY + userId + ":" + message.getType();
                    redisTemplate.opsForValue().decrement(typeRedisKey);
                }
            } catch (Exception e) {
                log.error("更新Redis缓存失败: {}", e.getMessage(), e);
                // Redis操作失败不影响主流程
            }
        }
        
        return convertToDTO(message);
    }
    
    @Override
    @Transactional
    public int markAllAsRead(Long userId, String type) {
        log.info("标记全部消息已读: userId={}, type={}", userId, type);
        LocalDateTime now = LocalDateTime.now();
        int updated;
        
        // 使用Mapper的自定义方法进行批量更新
        updated = messageMapper.markAllAsRead(userId, type, now);
        
        // 只有在Redis可用时才更新Redis缓存
        if (redisEnabled && redisTemplate != null) {
            try {
                if (type != null) {
                    // 重置Redis中的该类型未读消息计数
                    String typeRedisKey = UNREAD_TYPE_COUNT_KEY + userId + ":" + type;
                    redisTemplate.delete(typeRedisKey);
                } else {
                    // 重置Redis中的所有未读消息计数
                    String userCountKey = UNREAD_COUNT_KEY + userId;
                    redisTemplate.delete(userCountKey);
                    
                    // 由于没有一个简单的方式列出所有类型键，可以考虑不处理，
                    // 或者另外维护一个用户-消息类型的映射
                }
            } catch (Exception e) {
                log.error("更新Redis缓存失败: {}", e.getMessage(), e);
                // Redis操作失败不影响主流程
            }
        }
        
        log.info("已将{}条消息标记为已读", updated);
        return updated;
    }
    
    @Override
    public long getUnreadCount(Long userId, String type) {
        log.info("获取未读消息数量: userId={}, type={}", userId, type);
        
        // 尝试从Redis获取，如果可用的话
        if (redisEnabled && redisTemplate != null) {
            try {
                String redisKey;
                if (type != null) {
                    redisKey = UNREAD_TYPE_COUNT_KEY + userId + ":" + type;
                } else {
                    redisKey = UNREAD_COUNT_KEY + userId;
                }
                
                Object count = redisTemplate.opsForValue().get(redisKey);
                if (count != null) {
                    return Long.parseLong(count.toString());
                }
            } catch (Exception e) {
                log.error("从Redis获取未读消息数量失败: {}", e.getMessage(), e);
                // Redis获取失败，降级到数据库查询
            }
        }
        
        // 从数据库查询
        return messageMapper.countUnreadMessages(userId, type);
    }
    
    @Override
    @Transactional
    public void deleteMessage(Long messageId, Long userId) {
        log.info("删除消息: messageId={}, userId={}", messageId, userId);
        
        // 获取消息
        Message message = messageMapper.selectById(messageId);
        if (message == null) {
            throw new BusinessException("消息不存在");
        }
        
        // 验证消息归属
        if (!message.getReceiverId().equals(userId)) {
            throw new BusinessException("无权操作此消息");
        }
        
        // 删除消息
        messageMapper.deleteById(messageId);
        
        // 如果消息是未读的，则更新Redis计数
        if (!message.getIsRead() && redisEnabled && redisTemplate != null) {
            try {
                // 更新Redis中的未读消息计数
                String redisKey = UNREAD_COUNT_KEY + userId;
                redisTemplate.opsForValue().decrement(redisKey);
                
                // 更新特定类型的未读消息计数
                if (message.getType() != null) {
                    String typeRedisKey = UNREAD_TYPE_COUNT_KEY + userId + ":" + message.getType();
                    redisTemplate.opsForValue().decrement(typeRedisKey);
                }
            } catch (Exception e) {
                log.error("更新Redis缓存失败: {}", e.getMessage(), e);
                // Redis操作失败不影响主流程
            }
        }
        
        log.info("消息删除成功");
    }
    
    @Override
    @Transactional
    public int deleteAllMessages(Long userId) {
        log.info("删除用户所有消息: userId={}", userId);
        
        // 使用Mapper的自定义方法删除所有消息
        int deleted = messageMapper.deleteAllByReceiverId(userId);
        
        // 重置Redis计数
        if (redisEnabled && redisTemplate != null) {
            try {
                // 删除Redis中的未读消息计数
                String redisKey = UNREAD_COUNT_KEY + userId;
                redisTemplate.delete(redisKey);
                
                // 考虑删除所有类型的计数，但这需要知道所有类型
                // 这里可以通过保存用户的消息类型列表来实现，或者使用Redis的模式匹配
                String pattern = UNREAD_TYPE_COUNT_KEY + userId + ":*";
                // 假设有一个获取匹配键的方法
                // Set<String> keys = redisTemplate.keys(pattern);
                // redisTemplate.delete(keys);
            } catch (Exception e) {
                log.error("更新Redis缓存失败: {}", e.getMessage(), e);
                // Redis操作失败不影响主流程
            }
        }
        
        log.info("已删除{}条消息", deleted);
        return deleted;
    }
    
    @Override
    public MessageDTO convertToDTO(Message message) {
        if (message == null) {
            return null;
        }
        
        MessageDTO dto = new MessageDTO();
        BeanUtils.copyProperties(message, dto);
        
        // 获取发送者信息
        if (message.getSenderId() != null) {
            try {
                Map<String, Object> userInfo = userFeignClient.getUserBasicInfo(message.getSenderId());
                if (userInfo != null) {
                    dto.setSenderName((String) userInfo.get("username"));
                    dto.setSenderAvatar((String) userInfo.get("picture"));
                } else {
                    dto.setSenderName("用户_" + message.getSenderId());
                }
            } catch (Exception e) {
                log.error("获取发送者信息失败: {}", e.getMessage(), e);
                // 降级处理
                dto.setSenderName("用户_" + message.getSenderId());
            }
        } else {
            // 系统消息
            dto.setSenderName("系统通知");
        }
        
        return dto;
    }
    
    /**
     * 从事件构建消息实体
     */
    private Message buildMessageFromEvent(MessageEvent event) {
        return Message.builder()
                .type(event.getType().name())
                .content(event.getContent())
                .senderId(event.getSenderId())
                .receiverId(event.getReceiverId())
                .resourceId(event.getResourceId())
                .resourceType(event.getResourceType())
                .isRead(false)
                .link(event.getLink())
                .build();
    }
    
    /**
     * 检查是否是短时间内的重复消息
     * 用于防止同一事件短时间内重复发送
     */
    private boolean isDuplicateMessage(MessageEvent event) {
        LocalDateTime fiveMinutesAgo = LocalDateTime.now().minusMinutes(5);
        
        // 查找5分钟内相同类型、相同资源类型的消息
        List<Message> recentMessages = messageMapper.findRecentSimilarMessages(
                event.getReceiverId(), 
                event.getType().name(), 
                event.getResourceType(), 
                fiveMinutesAgo);
        
        // 检查是否有重复消息
        return recentMessages.stream()
                .anyMatch(msg -> 
                    // 相同发送者、接收者和资源ID
                    msg.getSenderId().equals(event.getSenderId()) &&
                    msg.getResourceId().equals(event.getResourceId())
                );
    }
} 